#include <render\threads\viewResolver.h>
#include <network\streamingClient.h>
#include <app\states\sessionState.h>
#include <app\states\moveState.h>
#include <app\states\rotateState.h>

#include <iostream>

void 
viewResolver::render() {

  gpu_mutex.lock();

    for (int l = 0; l != levels.size(); l++) {
      levels[l]->render(can_upload_gpu_data);
    }

    can_upload_gpu_data = false;

  gpu_mutex.unlock();

}

void 
viewResolver::move(const float x, const float y, const float z) {
  view_change_flag = true;
  cond.notify_one();
}

void 
viewResolver::rotate(const float x, const float y, const float z) {
  view_change_flag = true;
  cond.notify_one();
}

void 
viewResolver::viewChange(const viewingParametersState *vp_state_) {
  m_mutex.lock();

  vp_state.copy(vp_state_);

  for (int l = 0; l != levels.size(); l++) {
    delete levels[l];
  }

  all_loaded_tiles.clear();
  for (int l = vp_state.info.level_interval[1]; l >= vp_state.info.level_interval[0]; l--) {
    all_loaded_tiles.push_back(vector<boost::shared_ptr<tile> *>());
  }

  levels.clear();

  for (int l = vp_state.info.level_interval[1]; l >= vp_state.info.level_interval[0]; l--) {
    level *lvl = new level(l, &all_loaded_tiles, &vp_state);
    levels.push_back(lvl);
  }

  m_mutex.unlock();
}

void 
viewResolver::run() {

  moveState              *mov_state    = GET_STATE(moveState, streamingClient::instance().connection());
  rotateState            *rot_state    = GET_STATE(rotateState, streamingClient::instance().connection());
  viewingParametersState *dyn_vp_state = GET_STATE(viewingParametersState, streamingClient::instance().connection());
  terrainTileState *terrain_tile_state = GET_STATE(terrainTileState, streamingClient::instance().connection());
  textureTileState *texture_tile_state = GET_STATE(textureTileState, streamingClient::instance().connection());

  can_upload_gpu_data = false;

  while (!m_stoprequested) {

    if (can_upload_gpu_data) continue;

    boost::unique_lock<boost::mutex> lock(m_mutex);

    mov_state->pickSharedPosition();
    rot_state->pickSharedRotation();
    viewFrustum vf = dyn_vp_state->info.frustum;

    // prepare all levels with these values
    vec3f picked_position = mov_state->getSharedPosition();
    vec3f picked_rotation = rot_state->getSharedRotation();
    bool should_continue = true;

    s_download_query query;
    for (int l = 0; l != levels.size(); l++) {

      should_continue = levels[l]->prepare(picked_position, vf, should_continue, 0);
      
      if (should_continue /*|| istoplevel*/) {
        if (query.request_type == NOTHING) {

          query = levels[l]->generateDownloadQuery();
        
          if (query.request_type == TERRAIN && terrain_tile_state->isReady()) {
            query.tile_ptr->get()->selectedToDownload();
            c_request_terrain_tile *cmd = static_cast<c_request_terrain_tile *>(commandFactory::instance().createCommand(CLIENT_REQUEST_TERRAIN_TILE, streamingClient::instance().connection()));
            cmd->setData(query.tile_id.x, query.tile_id.y, query.level, query.tile_ptr);
            cmd->execute();
            delete cmd;
            view_change_flag = true;
          }
        }
      }
    }

    gpu_mutex.lock();
      can_upload_gpu_data = true;
    gpu_mutex.unlock();
    
    while(!view_change_flag) {
      cond.wait(lock);
    }

    view_change_flag = false;

    lock.unlock();

    //notify downloader
    // pass from coarser level to finer one
    // 1. select closest visible terrain tile with texture tile (one tile_id)
    // 2. download them both
    // 3. while not downloaded visible tiles are returned, goto 1
    // 4. pass to finer level
  }
}  

viewResolver::viewResolver() {
  view_change_flag = false;
//  m_stoprequested = false;
  
  vp_state.copy(GET_STATE(viewingParametersState, streamingClient::instance().connection()));
  s_view_parameters vp = vp_state.info;

  // create tiles downloader
  downloader = new downloadManager();

  levels.clear();

  for (int l = vp_state.info.level_interval[1]; l >= vp_state.info.level_interval[0]; l--) {
    all_loaded_tiles.push_back(vector<boost::shared_ptr<tile> *>());
  }

  // pass throug all levels from the coarser first (e.g. 5) to the finest last (e.g. 0)
  for (int l = vp.level_interval[1]; l >= vp.level_interval[0]; l--) {
    level *lvl = new level(l, &all_loaded_tiles, &vp_state);
    levels.push_back(lvl);
  }

  downloader->start();
}

void
viewResolver::stop() {
  assert(m_thread);
  m_stoprequested = true;

  m_mutex.lock();
   view_change_flag = true;
  m_mutex.unlock();

  cond.notify_one();

  m_thread->join();  
}

viewResolver::~viewResolver() {
  downloader->stop();
  delete downloader;

  for (int i = 0; i != levels.size(); i++) {
    delete levels[i];
  }
  levels.clear();
}